

/*
此代码对外提供了VGA控制信号，利用对时钟进行计数来判断当前是在扫描第几行的第几个像素，并确定是否要消隐。
代码输出的红R、绿G、蓝B三种颜色分别是以vga_r,vga_g,vga_b三个8位的二进制信号表示的，这三组8位数字信号将被传送到开发板上的数模转换器，转换成模拟信号，经VGA接口送入显示器中。
该控制器的特点是可以方便地实现上层系统对显示内容的控制。
例如，如果在模块调用时设置vga_data为常数24’hFF0000，就可以直接显示全屏红色。
上层系统也可以根据当前扫描的像素坐标，选择合适的颜色给不同的像素设置不同的vga_data。
更重要的是，上层系统可以分配一块显示存储，利用v_addr, h_addr来索引该显存，每次扫描到特定像素点时，按照显存的值来设置vga_data。
这样，其他应用就可以直接对显存进行操作，显存改变自动对应到VGA的显示上，而不用关心VGA扫描的具体过程了。
*/

// 定义名为vga_ctrl的模块，包含各种输入和输出端口
module vga_ctrl(
 input pclk,          // 25MHz的时钟信号
 input reset,         // 复位信号
 input [23:0] vga_data, // 上层模块提供的24位VGA颜色数据
 output [9:0] h_addr,  // 当前扫描像素点的水平坐标
 output [9:0] v_addr,  // 当前扫描像素点的垂直坐标
 output hsync,        // 行同步信号
 output vsync,        // 场同步信号
 output valid,        // 消隐信号，当处于消隐区时有效
 output [7:0] vga_r,   // 输出的红色颜色信号
 output [7:0] vga_g,   // 输出的绿色颜色信号
 output [7:0] vga_b    // 输出的蓝色颜色信号
 );

 // 定义640x480分辨率下的VGA参数
 parameter h_frontporch = 96;   // 行前肩区长度
 parameter h_active = 144;      // 行活跃区长度
 parameter h_backporch = 784;   // 行后肩区长度
 parameter h_total = 800;       // 一行的总像素数

 parameter v_frontporch = 2;    // 场前肩区长度
 parameter v_active = 35;       // 场活跃区长度
 parameter v_backporch = 515;   // 场后肩区长度
 parameter v_total = 525;       // 一场的总行数

 // 定义像素计数值的寄存器
 reg [9:0] x_cnt;  // 水平像素计数器
 reg [9:0] y_cnt;  // 垂直像素计数器
 wire h_valid;     // 水平消隐信号
 wire v_valid;     // 垂直消隐信号

 // 行像素计数的逻辑
 always @(posedge reset or posedge pclk) // 当reset或pclk的上升沿到来时执行
 if (reset == 1'b1) // 如果reset为1
 x_cnt <= 1;        // 重置x_cnt为1
 else               // 否则
 begin
 if (x_cnt == h_total) // 如果x_cnt达到一行的总像素数
 x_cnt <= 1;         // 重置x_cnt为1
 else                // 否则
 x_cnt <= x_cnt + 10'd1; // x_cnt自增1
 end

 // 列像素计数的逻辑
 always @(posedge pclk) // 当pclk的上升沿到来时执行
 if (reset == 1'b1) // 如果reset为1
 y_cnt <= 1;        // 重置y_cnt为1
 else               // 否则
 begin
 if (y_cnt == v_total & x_cnt == h_total) // 如果y_cnt达到一场的总行数且x_cnt为一行的总像素数
 y_cnt <= 1;        // 重置y_cnt为1
 else if (x_cnt == h_total) // 否则，如果x_cnt为一行的总像素数
 y_cnt <= y_cnt + 10'd1;    // y_cnt自增1
 end

 // 生成同步信号
 assign hsync = (x_cnt > h_frontporch); // 当x_cnt超过行前肩区时，hsync为1
 assign vsync = (y_cnt > v_frontporch); // 当y_cnt超过场前肩区时，vsync为1

 // 生成消隐信号
 assign h_valid = (x_cnt > h_active) & (x_cnt <= h_backporch); // 水平消隐发生在活跃区之后和后肩区之前
 assign v_valid = (y_cnt > v_active) & (y_cnt <= v_backporch); // 垂直消隐发生在活跃区之后和后肩区之前
 assign valid = h_valid & v_valid; // 当同时处于水平和垂直消隐区时，valid为1

 // 计算当前有效像素坐标
 assign h_addr = h_valid ? (x_cnt - 10'd145) : {10{1'b0}}; // 如果处于水平消隐区，h_addr为0，否则为x_cnt减去偏移量
 assign v_addr = v_valid ? (y_cnt - 10'd36) : {10{1'b0}}; // 如果处于垂直消隐区，v_addr为0，否则为y_cnt减去偏移量

 // 设置输出的颜色值
 assign vga_r = vga_data[23:16]; // 提取红色分量
 assign vga_g = vga_data[15:8];  // 提取绿色分量
 assign vga_b = vga_data[7:0];   // 提取蓝色分量

endmodule